home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 398 / 398.xpi / chrome / forecastfox.jar / content / profiles / profile-service.js < prev    next >
Text File  |  2010-02-04  |  20KB  |  757 lines

  1. /*------------------------------------------------------------------------------
  2.   Copyright (c) 2008 Ensolis, LLC. All Rights Reserved.
  3.   ----------------------------------------------------------------------------*/
  4.   
  5. /******************************************************************************
  6.  * load a list of preferences into an object.  Used in setting up items.
  7.  *
  8.  * @return  An object hold preference names and value.
  9.  ******************************************************************************/
  10. function getDefaultPrefs()
  11. {
  12.   //set the return variable
  13.   var rv = {};
  14.   var sorter = [];
  15.     
  16.   //get the default pref branch and its children
  17.   var branch = getBranch(true, null);
  18.   var prefs = branch.getChildList("", {});
  19.     
  20.   //loop through prefs
  21.   for (var i=0; i<prefs.length; i++) {
  22.     var pref = prefs[i];
  23.       
  24.     //skip excluded
  25.     if (EXCLUDED_PREFS.hasOwnProperty(pref) &&
  26.         EXCLUDED_PREFS[pref] == true)
  27.       continue;
  28.  
  29.     //get the preference value
  30.     var value = "";
  31.     try {        
  32.       switch (branch.getPrefType(pref)) {
  33.       case Ci.nsIPrefBranch.PREF_INT:
  34.         value = branch.getIntPref(pref);
  35.         break;
  36.       case Ci.nsIPrefBranch.PREF_BOOL:
  37.         value = branch.getBoolPref(pref);
  38.         break;
  39.       case Ci.nsIPrefBranch.PREF_STRING:
  40.       default:
  41.         try {
  42.           value = branch.getComplexValue(pref, Ci.nsIPrefLocalizedString).data;                         
  43.         } catch(e) {
  44.           value = branch.getCharPref(pref);
  45.         }
  46.         break;    
  47.       }
  48.        
  49.       //add to array  
  50.       sorter.push({ "name": pref, "value": value});
  51.       
  52.     //value does not exist in default so remove it
  53.     } catch (e) {
  54.       var userBranch = getBranch(false, null);
  55.       if (userBranch.prefHasUserValue(pref))
  56.         userBranch.clearUserPref(pref);
  57.     }      
  58.   }
  59.   
  60.   // sort the array and add to object
  61.   sorter.sort(sortByName);
  62.   while (sorter.length) {
  63.     var item = sorter.shift();
  64.     rv[item.name] = item.value;
  65.   }
  66.     
  67.   //return the variable
  68.   return rv;
  69. }
  70.   
  71. /******************************************************************************
  72.  * Interface for describing the profile service.  This service should 
  73.  * be used for all profile data related changes.  
  74.  * Migration, import, and export is handled by the migrator service.
  75.  * 
  76.  * @status    FROZEN
  77.  * @version   1.0
  78.  ******************************************************************************/
  79. function ProfileService() 
  80. {
  81.   //setup additional interfaces
  82.   this._ifaces.push(Ci.nsIObserver);
  83.   this._ifaces.push(Ci.nsISupportsWeakReference);
  84.   
  85.   //setup a new error
  86.   this._error = Cc["@ensolis.com/forecastfox/error-item;1"].
  87.                 createInstance(Ci.ffIErrorItem);     
  88. }
  89. ProfileService.prototype = {
  90.   __proto__: new ServiceBase("ProfileService"),
  91.   _obSvc: null,
  92.   _items: null,
  93.   _rotateTimer: null,
  94.   _isBatch: null,
  95.   _isRotating: null,
  96.   _isLoading: null,
  97.   _flushPending: null,
  98.   _defaultPrefs: null,
  99.  
  100.   ///////////////////////////
  101.   // nsIObserver
  102.   observe: function ProfileService_observe(aSubject, aTopic, aData)
  103.   {
  104.     switch (aTopic) {
  105.      
  106.     //timer fired
  107.     case "timer-callback":
  108.     
  109.       //rotating fired so switch profiles
  110.       if (aSubject == this._rotateTimer) {
  111.         this.current = this._nextItem();  
  112.         this._scheduleTimer();   
  113.       }
  114.       break;
  115.       
  116.     //preference changed
  117.     case "nsPref:changed":
  118.  
  119.       //ignore changes if we are loading
  120.       if (this.isLoading)
  121.         return;
  122.       
  123.       //current changed
  124.       if (aData == "profile.current") {
  125.         this.current = this.getItem(getPref(aData));
  126.         return;
  127.       }
  128.       
  129.       //keep properties in sync with prefs
  130.       var id = this.current.ID;
  131.       this._items[id].setProperty(aData, getPref(aData));
  132.       this._flushData();
  133.       
  134.       //rotating prefs changed
  135.       if (aData == "profile.switch.enabled" ||
  136.           aData == "profile.switch.delay") {
  137.           
  138.         //start rotating if we are not in batch mode
  139.         if (!this.isBatch)     
  140.           this.startRotating();
  141.         return;
  142.       }  
  143.       break;  
  144.     }
  145.   },
  146.                 
  147.   ////////////////////////////////
  148.   // ffIService        
  149.   
  150.   /**
  151.    * Initialize the component.  Called by the manager service.  Returns false
  152.    * if the datasource could not be loaded.  Chech the lastError property for
  153.    * more information.
  154.    */
  155.   start: function ProfileService_start()
  156.   {
  157.     //setup the variables
  158.     this._items = {};
  159.     this._isBatch = false;
  160.     this._isRotating = false;
  161.     this._isLoading = false;
  162.     this._flushPending = false;
  163.     
  164.     //get the disk and migrator services
  165.     var mgrSvc = Cc["@ensolis.com/forecastfox/manager-service;1"].
  166.                  getService(Ci.ffIManagerService);
  167.     var migSvc = mgrSvc.migrator;
  168.     
  169.     //get the observer service
  170.     this._obSvc = Cc["@mozilla.org/observer-service;1"].
  171.                   getService(Ci.nsIObserverService);
  172.     
  173.     //setup the timer with 10 minute repeat timer
  174.     this._rotateTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  175.     
  176.     //get the list of preference
  177.     this._defaultPrefs = getDefaultPrefs();
  178.     
  179.     //start batch mode
  180.     this.startBatch();
  181.     
  182.     //load profiles using the migration service
  183.     var success = migSvc.migrate(this);
  184.     if (!success) {
  185.       var err = migSvc.lastError;
  186.       this._error.init(err.severity, err.name, err.message);
  187.     }
  188.     
  189.     //end batch mode
  190.     this.endBatch();
  191.     
  192.     //setup a preference observer
  193.     var pb2 = this.branch.QueryInterface(Ci.nsIPrefBranchInternal);
  194.     pb2.addObserver("", this, true);
  195.     
  196.     //return our success code
  197.     return success;                        
  198.   },
  199.   
  200.   /**
  201.    * Destroy the component.  Called by the manager service.  This may be
  202.    * called prior to start so it needs to be safe.
  203.    */
  204.   stop: function ProfileService_stop()
  205.   {
  206.     //remove the pref observer
  207.     var pb2 = this.branch.QueryInterface(Ci.nsIPrefBranchInternal);
  208.     pb2.removeObserver("", this);
  209.        
  210.     //stop the rotating timer
  211.     if (this._rotateTimer)
  212.       this._rotateTimer.cancel();   
  213.     
  214.     //clear variables
  215.     this._obSvc = null;
  216.     this._items = null;
  217.     this._rotateTimer = null;
  218.     this._flushTimer = null;
  219.     this._isBatch = null;
  220.     this._isRotating = null;
  221.     this._isLoading = null;
  222.     this._flushPending = null;
  223.     this._defaultPrefs = null;
  224.   },
  225.                 
  226.   ////////////////////////////////
  227.   // ffIProfileService  
  228.      
  229.   /**
  230.    * Currently selected profile item.
  231.    */
  232.   get current() { return this._ensureItem(); }, 
  233.   
  234.   set current(aVal) {
  235.     //do nothing if null passed in
  236.     if (!aVal)
  237.       return;
  238.     
  239.     //do nothing if we don't have it
  240.     if (!this.hasItem(aVal.ID))
  241.       return;
  242.         
  243.     //load the items properties
  244.     this._items[aVal.ID] = aVal.clone();
  245.     this._loadItem(aVal);
  246.     
  247.     //notify observers
  248.     this._obSvc.notifyObservers(aVal, "forecastfox-profiles", "current");
  249.   },
  250.   
  251.   /**
  252.    * Profile service is in batch mode.
  253.    */
  254.   get isBatch() { return this._isBatch; },
  255.   
  256.   /**
  257.    * Profile service is in rotating mode.
  258.    */
  259.   get isRotating() { return this._isRotating; },
  260.   
  261.   /**
  262.    * Profile is being loaded into preferences.
  263.    */
  264.   get isLoading() { return this._isLoading; },
  265.       
  266.   /**
  267.    * Profiles has a specific item. 
  268.    * 
  269.    * @param   ID of the profile item.
  270.    * @return  True if item exists.
  271.    */
  272.   hasItem: function ProfileService_hasItem(aID)
  273.   {
  274.     return this._items.hasOwnProperty(aID);
  275.   },
  276.                            
  277.   /**
  278.    * Retrieve a profile item returns null if item doesn't exist.
  279.    * 
  280.    * @param   ID of the profile item.
  281.    * @return  A ffIProfileItem.
  282.    */
  283.   getItem: function ProfileService_getItem(aID)
  284.   {
  285.     if (!this.hasItem(aID))
  286.       return null;
  287.     else
  288.       return this._items[aID];
  289.   },
  290.    
  291.   /**
  292.    * Retrieve an array of profile items.
  293.    * 
  294.    * @param   Count of items in the array.
  295.    * @return  An array of ffIProfileItem components.
  296.    */
  297.   getItems: function ProfileService_getItems(aCount)
  298.   {    
  299.     //loop through items
  300.     var items = [];
  301.     for (var id in this._items)
  302.       items.push(this._items[id]);
  303.  
  304.     //sort the array
  305.     items.sort(sortByName);
  306.         
  307.     //return the array
  308.     aCount.value = items.length;
  309.     return items; 
  310.   },
  311.                   
  312.   /**
  313.    * Sets a profile item.
  314.    *
  315.    * @param   The profile item.
  316.    */
  317.   setItem: function ProfileService_setItem(aItem)
  318.   {
  319.     //set the item
  320.     this._items[aItem.ID] = aItem.clone();
  321.     
  322.     //if the item is current then load it
  323.     if (!this.isBatch && this.current.ID == aItem.ID)
  324.       this._loadItem(aItem);
  325.     
  326.     //flush async
  327.     this._flushData();
  328.      
  329.     //notify observers
  330.     this._obSvc.notifyObservers(this, "forecastfox-profiles", "setItem");
  331.   },
  332.      
  333.   /**
  334.    * Remove a profile item.  It also removes that profiles data cache.
  335.    * 
  336.    * @param   ID of the profile item.
  337.    */
  338.   deleteItem: function ProfileService_deleteItem(aID)
  339.   {
  340.     //do nothing if we don't have item
  341.     if (!this.hasItem(aID))
  342.       return;
  343.     
  344.     //if the item is the current then load the next one
  345.     if (!this.isBatch && this.current.ID == aID)
  346.       this.current = this._nextItem();
  347.       
  348.     //remove the item
  349.     delete this._items[aID];
  350.      
  351.     //remove the items cache
  352.     var mgrSvc = Cc["@ensolis.com/forecastfox/manager-service;1"].
  353.                  getService(Ci.ffIManagerService);
  354.     var dskSvc = mgrSvc.disk;
  355.     var feed = dskSvc.get("feed-" + aID + ".js", TYPE_CACHE);
  356.     var radar = dskSvc.get("radar-" + aID + ".gif", TYPE_CACHE);
  357.     try {
  358.       removeFile(feed);
  359.       removeFile(radar);
  360.     } catch(e) {}
  361.      
  362.     //flush async
  363.     this._flushData();
  364.      
  365.     //notify observers
  366.     this._obSvc.notifyObservers(this, "forecastfox-profiles", "deleteItem");  
  367.   },
  368.                        
  369.   /**
  370.    * Turn on batch mode. Observers will get a notification of this.
  371.    * They will still get notified for every individual change, but they are
  372.    * free to ignore those notifications.
  373.    * Use this when a lot of changes are about to happen, and it would be
  374.    * useless to refresh the display (or the backend store) for every change.
  375.    * Caller must make sure to also call endBatch. Make sure all errors
  376.    * are caught!
  377.    */                     
  378.   startBatch: function ProfileService_startBatch()
  379.   {
  380.     //do nothing if already batch
  381.     if (this.isBatch)
  382.       return;
  383.  
  384.     //stop rotating
  385.     this.endRotating();
  386.          
  387.     //mark batch
  388.     this._isBatch = true; 
  389.          
  390.     //notify observers
  391.     this._obSvc.notifyObservers(this, "forecastfox-profiles", "startBatch"); 
  392.   },
  393.    
  394.   /**
  395.    * Turn off batch mode.
  396.    */  
  397.   endBatch: function ProfileService_endBatch()
  398.   {
  399.     //do nothing if not batch
  400.     if (!this.isBatch)
  401.       return;
  402.     
  403.     //make sure we have a current profile
  404.     if (!this.hasItem(getPref("profile.current")))
  405.       this.current = this._ensureItem();
  406.     else
  407.       this._loadItem(this.current);
  408.             
  409.     //mark not batch
  410.     this._isBatch = false; 
  411.    
  412.     // flush any pending changes
  413.     if (this._flushPending) {
  414.       this._flushData();
  415.       this._flushPending = false;
  416.     }
  417.     
  418.     //notify observers
  419.     this._obSvc.notifyObservers(this, "forecastfox-profiles", "endBatch");
  420.      
  421.     //start rotating
  422.     this.startRotating();     
  423.   },
  424.                      
  425.   /**
  426.    * Turn on rotating mode. 
  427.    */                     
  428.   startRotating: function ProfileService_startRotating()
  429.   {
  430.     //do nothing if already rotating
  431.     if (this.isRotating)
  432.       return;
  433.      
  434.     //mark rotating
  435.     this._isRotating = true; 
  436.         
  437.     //start the timer
  438.     this._scheduleTimer();
  439.   },
  440.    
  441.   /**
  442.    * Turn off rotating mode.
  443.    */  
  444.   endRotating: function ProfileService_endRotating()
  445.   {
  446.     //do nothing if not rotating
  447.     if (!this.isRotating)
  448.       return;
  449.      
  450.     //mark not rotating
  451.     this._isRotating = false; 
  452.     
  453.     //stop the timer
  454.     this._rotateTimer.cancel();     
  455.   },
  456.   
  457.   /**  
  458.    * Create a profile item from a DOM node.
  459.    * 
  460.    * @param   The profile DOM node.
  461.    * @return  A profile item.
  462.    */
  463.   createItem: function ProfileService__createItem(aNode)
  464.   {
  465.     //create an empty profile item
  466.     var item = Cc["@ensolis.com/forecastfox/profile-item;1"].
  467.                createInstance(Ci.ffIProfileItem);
  468.     
  469.     //set the name
  470.     if (aNode.hasAttribute("name"))
  471.       item.setProperty("name", aNode.getAttribute("name"));
  472.     else
  473.       item.setProperty("name", aNode.getAttribute("id"));
  474.     
  475.     //set the ID
  476.     item.setProperty("ID", aNode.getAttribute("id"));
  477.     
  478.     //loop through pref nodes
  479.     var nodes = aNode.getElementsByTagName("pref");
  480.     for (var i=0; i<nodes.length; i++) {
  481.       var node = nodes[i];
  482.       
  483.       //get the node attributes
  484.       var name = node.getAttribute("name");
  485.       var value = node.getAttribute("value");
  486.       var type = node.getAttribute("type");
  487.       
  488.       //set the property based on type
  489.       switch (type) {
  490.       case "Int":
  491.         item.setProperty(name, Number(value));
  492.         break;
  493.       case "Bool":
  494.         item.setProperty(name, (value == "true"));
  495.         break;
  496.       case "Char":
  497.       default:
  498.         item.setProperty(name, value);
  499.         break;
  500.       }
  501.     }
  502.     
  503.     //make sure any prefs not set are now
  504.     for (var pref in this._defaultPrefs) {
  505.       if (!item.hasProperty(pref))
  506.           item.setProperty(pref, this._defaultPrefs[pref]);
  507.     }
  508.     
  509.     //return the item
  510.     return item;
  511.   },
  512.   
  513.   /**
  514.    * Create a DOM node from a profile item.
  515.    * 
  516.    * @param   The profile item.
  517.    * @param   The owner document of the node.
  518.    * @return  The DOM node.
  519.    */
  520.   createNode: function ProfileService_createNode(aItem, aDoc)
  521.   {
  522.     //create a profile node
  523.     var profile = aDoc.createElementNS(PROFILES_NS, "profile");
  524.     
  525.     //set the name and id
  526.     profile.setAttribute("id", aItem.ID);
  527.     profile.setAttribute("name", aItem.name);
  528.     
  529.     //loop through the preferences
  530.     for (var pref in this._defaultPrefs) {
  531.       
  532.       //get the preference value
  533.       var value = "";
  534.       if (aItem.hasProperty(pref))
  535.         value = aItem.getProperty(pref);
  536.       else
  537.         value = this._defaultPrefs[pref];
  538.        
  539.       //set the node properties
  540.       var node = aDoc.createElementNS(PROFILES_NS, "pref");
  541.       node.setAttribute("name", pref);
  542.       node.setAttribute("value", String(value));
  543.       if (typeof value == "number")
  544.         node.setAttribute("type", "Int");
  545.       else if (typeof value == "boolean")
  546.         node.setAttribute("type", "Bool");
  547.       else
  548.         node.setAttribute("type", "Char");
  549.       
  550.       //append it to the profile
  551.       profile.appendChild(node);          
  552.     }
  553.     
  554.     //return the node
  555.     return profile;
  556.   },
  557.   
  558.   /**
  559.    * Create a unique id for a profile item.
  560.    * 
  561.    * @return  A unique uuid.
  562.    */
  563.   createID: function ProfileService_createID()
  564.   {
  565.     //use the uuid generator if it exists
  566.     var generator = Cc["@mozilla.org/uuid-generator;1"].
  567.                     getService(Ci.nsIUUIDGenerator);
  568.     
  569.     //create an id
  570.     var id = generator.generateUUID().toString();
  571.     
  572.     //loop while the id is not unique
  573.     while (this.hasItem(id))
  574.       id = generator.generateUUID().toString();
  575.  
  576.     //return the id
  577.     return id;
  578.   },
  579.   
  580.   /**
  581.    * Recreate an item from current preferences.
  582.    * 
  583.    * @return  A ffIProfileItem.
  584.    */
  585.   recreateItem: function ProfileService_recreateItem()
  586.   {
  587.     //create a new item
  588.     var item = Cc["@ensolis.com/forecastfox/profile-item;1"].
  589.                createInstance(Ci.ffIProfileItem);
  590.     
  591.     //loop through prefs and set the property
  592.     for (var pref in this._defaultPrefs)    
  593.       item.setProperty(pref, getPref(pref));
  594.     
  595.     //set the name and id
  596.     item.setProperty("ID", getPref("profile.current"));
  597.     item.setProperty("name", item.getProperty("ID"));
  598.       
  599.     //return the item
  600.     return item;
  601.   },
  602.                       
  603.   ////////////////////////////////
  604.   // Internal Functions  
  605.  
  606.   /**
  607.    * Flush changes to disk.
  608.    *
  609.    * @param   Call file writer in blocking mode.
  610.    */
  611.   _flushData: function ProfileService__flushData()
  612.   {
  613.     //do nothing if in batch mode
  614.     if (this.isBatch) {
  615.       this._flushPending = true;
  616.       return;
  617.     }
  618.     
  619.     //get the profiles file
  620.     var mgrSvc = Cc["@ensolis.com/forecastfox/manager-service;1"].
  621.                  getService(Ci.ffIManagerService);
  622.     var dskSvc = mgrSvc.disk;
  623.     var file = dskSvc.get("profiles.xml", TYPE_PROFILE);
  624.     
  625.     //do nothing if not writable
  626.     if ((!file.exists() && !file.parent.isWritable()) ||
  627.         (file.exists() && !file.isWritable())) {
  628.       dskSvc.log("Profile file is not writable: " + file.path, null, null);
  629.       return;
  630.     }
  631.      
  632.     //create a new profiles document
  633.     var doc = dskSvc.create("profiles", PROFILES_DTD, PROFILES_NS);
  634.     
  635.     //loop through items
  636.     for (var id in this._items) {
  637.       var item = this._items[id];
  638.       
  639.       //create the node
  640.       var node = this.createNode(item, doc);
  641.       
  642.       //append it to the document
  643.       doc.documentElement.appendChild(node);
  644.     }
  645.      
  646.     //write to disk
  647.     dskSvc.write(file, doc, true, false);
  648.   },
  649.    
  650.   /**
  651.    * Schedule the rotation timer.
  652.    */
  653.   _scheduleTimer: function ProfileService__scheduleTimer()
  654.   {
  655.     //get the rotating prefs
  656.     var enabled = getPref("profile.switch.enabled");
  657.     var delay = getPref("profile.switch.delay");
  658.     delay *= 60*1000;
  659.     
  660.     //not enabled or no delay
  661.     if (!enabled || delay == 0)
  662.       this.endRotating();
  663.       
  664.     //start the timer
  665.     else
  666.       this._rotateTimer.init(this, delay, Ci.nsITimer.TYPE_ONE_SHOT);  
  667.   },
  668.    
  669.   /**
  670.    * Select the next profile item and schedule the timer.
  671.    */
  672.   _nextItem: function ProfileService__nextItem()
  673.   {
  674.     //setup variables
  675.     var ids = [];
  676.     var index = 0;
  677.     var current = this.current.ID;
  678.     var found = false;
  679.     
  680.     //loop through items
  681.     for (var id in this._items) {
  682.     
  683.       //mark that we found the current
  684.       if (current == id)
  685.         found = true;
  686.         
  687.       //increment the index
  688.       if (!found)
  689.         index++;  
  690.         
  691.       //save the id
  692.       ids.push(id);  
  693.     }
  694.     
  695.     //return the next item
  696.     index = (index + 1 < ids.length)  ? index + 1 : 0;
  697.     return this._items[ids[index]];
  698.   },
  699.   
  700.   /**
  701.    * Load an item into preferences.
  702.    *
  703.    * @param   The item to load.
  704.    */
  705.   _loadItem: function ProfileService__loadItem(aItem)
  706.   {
  707.     //mark we are loading
  708.     this._isLoading = true;
  709.     
  710.     //loop through the preferences
  711.     for (var pref in this._defaultPrefs) {
  712.             
  713.       //set the preference if the item has it
  714.       if (aItem.hasProperty(pref))
  715.         setPref(pref, aItem.getProperty(pref));
  716.         
  717.       //use the default preference value
  718.       else
  719.         setPref(pref, this._defaultPrefs[pref]);  
  720.     }
  721.     
  722.     //set the current pref
  723.     setPref("profile.current", aItem.ID);  
  724.       
  725.     //mark we are finished loading
  726.     this._isLoading = false;    
  727.   },
  728.   
  729.   /**
  730.    * Ensure there is an item available for the current property.
  731.    *
  732.    * @return  A profile item.
  733.    */
  734.   _ensureItem: function ProfileService__ensureItem()
  735.   {
  736.     //get the current item
  737.     var item = this.getItem(getPref("profile.current"));
  738.     if (!item) {
  739.  
  740.       //get all items
  741.       var items = this.getItems({});
  742.       
  743.       //set to the first one
  744.       if (items.length > 0)
  745.         item = items[0];
  746.         
  747.       //recreate it from preferences
  748.       else {
  749.         item = this.recreateItem();
  750.         this.setItem(item);
  751.       }
  752.     }
  753.     
  754.     //return the item
  755.     return item;  
  756.   }
  757. };